<?php
/***
 * Candy框架 内置的函数库
 * 
 * $Author: 刘森 (fingerboy@qq.com) $
 * $Date: 2019-07-25 01:56:31 $   
 */

defined('CANDY') OR die('You Are A Bad Guy. o_O???');

/**
 * 异步调用实例化类
 */
function A(string|null $className = null):mixed
{
	if(is_null($className)){
		\Candy\Core\Debug::addMsg('<font color=\'red\'>必须为A()函数提供参数!');
		return false;
	}else{
		$app = null;
		$actionName = null;
		$newapp = false;
		if(strpos($className, '://')){
			list($app, $className) = explode('://', $className);
			//不是同一个APP
			$newapp = true;
		}
		
		//检查appname
		is_null($app) && $app = C('APPNAME');
		
		//解析控制器
		if(strpos($className, '/')){
			list($className, $actionName) = explode('/', $className, 2);
		}
		
		//没有传递控制器名称
		if(is_null($actionName)){
			$actionName = $className;
			$className = C('CONTROL');
		}
		
		//对象名称
		$classObjName = 'APP\\Control\\'. $app . '\\' . ucfirst(strtolower($className)).'Action';
		
		//重置项目路径
		$app_path = CANDYROOT.'Application/'. $app .'/';
		C('APP_PATH', $app_path);
		
		//跨项目调用
		if($newapp){
			if($app == 'Addon'){
				$app = $className;
				//插件项目
				$is_addon = \Candy\Core\Addon::checkAddon($app);
				if($is_addon){
					C('ISADDON', true);
					
					//重新定义项目
					C('APPNAME', $app);
					
					//重置项目路径
					$app_path = CANDYROOT.'Addons/'. $app .'/';
					C('APP_PATH', $app_path);
					
					//加载项目公用父类 common
					$commonpath = RUNTIME .'Controls/Addons/'. $app . '/' . strtolower($app).'common.class.php';
					$inres = requireCache($commonpath);
					if($inres){
						\Candy\Core\Debug::addMsg('<b> '. ucfirst($app) .'Common </b>类', 1);
					}
					
					//再次分化出控制器和操作
					list($className, $actionName) = explode('/', $actionName, 2);
					
					//加载控制器类
					$classpath = RUNTIME .'Controls/Addons/'. $app . '/' . strtolower($className).'action.class.php';
					
					//重新定义对象名称
					$classObjName = 'APP\\Addon\\' . ucfirst(strtolower($app)) .'\\' . $className .'Action';
					
					$inres = requireCache($classpath);
					if($inres){
						\Candy\Core\Debug::addMsg('<b> '. ucfirst($className) .' </b>类', 1);
					}
				}else{
					\Candy\Core\Debug::showMessage($className .'插件不存在！', 'Shutdown');
				}
			}else{
				//加载项目公用父类 common
				$commonpath = RUNTIME.'Controls/'. $app .'/'. strtolower($app).'common.class.php';
				$inres = requireCache($commonpath);
				if($inres){
					\Candy\Core\Debug::addMsg('<b> '. ucfirst($app) .'Common </b>类', 1);
				}
				
				//加载控制器类
				$classpath = RUNTIME.'Controls/'. $app .'/'. strtolower($className).'action.class.php';
				$inres = requireCache($classpath);
				if($inres){
					\Candy\Core\Debug::addMsg('<b> '. ucfirst($className) .' </b>类', 1);
				}
			}
		}
		
		//重置语言库
		\Candy\Core\Debug::addMsg('[<b>Languages file</b>] 语言库重置为 ：'. $app .'项目', 3);
		$LANG = [];
		$clangfile = C('APP_PATH') .'Languages/'. G('LANG') .'/Common.lang.php'; //项目公共语言库
		if(fileExistsCase($clangfile))
			include($clangfile);

		//操作语言库
		$langfile = C('APP_PATH') .'Languages/'. G('LANG') .'/'. ucfirst(strtolower($_GET['m'])).'.lang.php';
		if(fileExistsCase($langfile))
			include($langfile);

		$lang_path = str_replace(CANDYROOT, '/', $langfile);
		\Candy\Core\Debug::addMsg('[<b>Languages file</b>] 当前项目语言库路径：'. $lang_path, 3);
		//储存语言
		C('LANGINFO', $LANG);
		
		//创建对象
		$classObj = N($classObjName);
		
		//重置操作
		$classObj->control = $_GET['m'] = $className;
		$classObj->action = $_GET['a'] = $actionName;
		
		//重置模版类库
		initTplPath($app);
		
		//运行
		$classObj->working();
	}
}

/**
 * 添加IP黑、白名单
 */
function addIPList(string $type = 'b', string $ip = '',int $exp_time = 0):bool
{
	$data['type'] = $type;
	if(is_array($ip)){
		[$data['ip_start'], $data['ip_end']] = $ip;
	}else{
		$data['ip_start'] = $ip;
	}
	return \Candy\Core\Security::add($data);
}

/**
 * 分配api变量
 */
function apiDate(array $data):array
{
	C('APIDATA', $data);
	return $data;
}

/**
 *分配变量
 */
function assign(array $params, &$view_vars):void
{
	extract($params);
	if(function_exists($value)){
		unset($params['var']);
		unset($params['value']);
		$view_vars[$var] = $value($params);
	}else{
		$view_vars[$var] = $value;
	}
}

/**
 * 获取和设置配置参数 支持批量定义
 *
 * @param string|array $name 配置变量
 * @param mixed $value 配置值
 * @param mixed $default 默认值
 * @return mixed
 */
function C(string|array|null $name=null,mixed $value='',mixed $default=null):mixed
{
	//workerman模式下用sessionid实现数据分离
	if(defined('CLIWORKING')){
		$response = \Candy\Core\Container::getInstance()->get('Response');
		if(is_null($response)) return null; 
		// 无参数时获取所有
		if (empty($name)){
			return $response->staticAgrs;
		}
		// 优先执行设置获取或赋值
		if(is_string($name)){
			if(!strpos($name, '.')){
				$name = strtoupper($name);
				if($value === '')
					return isset($response->staticAgrs[$name]) ? $response->staticAgrs[$name] : $default;
				
				$response->staticAgrs[$name] = $value;
				return null;
			}
			// 二维数组设置和获取支持
			$name = explode('.', $name);
			$name[0] = strtoupper($name[0]);
			if($value === '')
				return isset($response->staticAgrs[$name[0]][$name[1]]) ? $response->staticAgrs[$name[0]][$name[1]] : $default;
			
			$response->staticAgrs[$name[0]][$name[1]] = $value;
			return null;
		}
		
		// 批量设置
		if (is_array($name)){
			$response->staticAgrs = array_merge($response->staticAgrs, array_change_key_case($name,CASE_UPPER));
			return null;
		}
		
		return null; // 避免非法参数
	}
	
	return S('c', $name, $value, $default);
}

/**
 * 开启缓存
 */
function cacheLoading(int $cstart = 0,string $type=''):null|object
{
	//运行模式
	defined('CLIWORKING') OR \Candy\Core\Debug::addMsg('<font color=\'red\'>建议使用Cli模式运行!</font>');
	//判断是否开启了页面静态化缓存
	$cache = $cstart === 1 ? '开启页面缓存，实现页面静态化!' : '<font color=\'red\'>没有开启页面缓存!</font>（但可以使用）';
	\Candy\Core\Debug::addMsg($cache);
	
	//启用memcache缓存
	$cache = null;
	if(!empty($type)){
		if(!extension_loaded($type)){
			\Candy\Core\Debug::addMsg('<font color=\'red\'>PHP没有安装'. $type .'扩展模块,请先安装!</font>');
		}else{
			$cache = \Candy\Extend\Cache\CacheServer::instance();
			if(!$cache->cacheConnectError()){
				\Candy\Core\Debug::addMsg('<font color=\'red\'>连接'. $mtype .'服务器失败,请检查!</font>');
				$cache = null;
			}else{
				//设置cache的全局变量
				\Candy\Core\Debug::addMsg('启用了'. $type);
			}
		}
	}else{
		\Candy\Core\Debug::addMsg('<font color=\'red\'>没有使用Memcache(d)或Redis</font>(为程序的运行速度，建议使用Memcache(d)或Redis)');
	}
	
	return $cache;
}

/**
 * 检查节点权限
 * @param $node
 * @param array $auths
 * @return boolean
 */
function checkAuth(string|null $node = null,array $auths = []): bool
{
	return \Candy\Core\Node::forceAuth($node, $auths);
}

/**
 * 检查IP名单
 * @param $type
 * @param $ip
 * @return boolean
 */
function checkIP(string $type = 'b',string $ip = ''): bool
{
	return \Candy\Core\Security::check($type, $ip);
}

/**
 * 获取IP列表
 */
function cleanIPList(string $type = '',bool $reset = false): void
{
	\Candy\Core\Security::clean($type, $reset);
}

/**
 * 清除缓存文件
 */
function cleanRuntime(): void
{
	\Candy\Extend\Dir::del(RUNTIME.'Cache');	//清除缓存文件
	\Candy\Extend\Dir::del(RUNTIME.'Comps');	//清除模版编译文件
	\Candy\Extend\Dir::del(RUNTIME.'Controls');	//清除控制器编译文件
	\Candy\Extend\Dir::del(RUNTIME.'Data');	//清除数据编译文件
	\Candy\Extend\Dir::del(RUNTIME.'Logs');	//清除Log文件
	\Candy\Extend\Dir::del(RUNTIME.'Models');	//清除模型编译文件
}

/**
 * cookie别名
 */
function cookie(string $name = '',mixed $value = '',int $expire = 0,string $path = '/',string $domain = ''):mixed
{
	if($domain === '') $domain = $_SERVER['HTTP_HOST'];
	
	//cookie对象
	$ckobj = null;
	if(defined('CLIWORKING')){
		if($value === ''){
			$ckobj = \Candy\Core\Container::getInstance()->get('Request');
		}else{
			$ckobj = \Candy\Core\Container::getInstance()->get('Response');
		}
	}
	
	//清除
	if(is_null($value)){
		if(empty($name)){
			//清除所有
			if(is_null($ckobj)){
				unset($_COOKIE);
			}else{
				$ckobj->cookie('');
			}
		}else{
			//清除指定
			if(is_null($ckobj)){
				setrawcookie($name, '', '-3600', $path, $domain);
				unset($_COOKIE[$name]);
			}else{
				$ckobj->cookie($name, '', '-3600');
			}
		}
		return true;
	}
	
	if($value === ''){
		if(is_null($ckobj)){
			return isset($_COOKIE[$name]) ? unserialize(base64_decode($_COOKIE[$name])) : false;
		}else{
			return is_null($ckobj->cookie($name)) ? null : unserialize(base64_decode($ckobj->cookie($name)));
		}
	}else{
		$value = base64_encode(serialize($value));  //数据加密
		$time = gmTime();
		$expire = $expire == 0 ? $time+3600 : $time+$expire;  //默认有效时间一小时
		if(is_null($ckobj)){
			setrawcookie($name, $value, $expire, $path, $domain);
			$_COOKIE[$name] = $value;
		}else{
			$ckobj->cookie($name, $value, $expire);
		}
		return true;
	}
}

/**
 * 字符串中符号信息
 *
 *  @param	string	$str 字符串
 *  @param	array	$protectSeparator 保护符号
 *  @return	array
 */
function ctypeAlnumInfo(string $str='',array $protectSeparator = []):array
{
	if(empty($str))	return [];
	$i = 0;
	while(!empty($str) && (ctype_alnum($str[0]) || in_array($str[0], $protectSeparator))){
		$str = substr($str, 1);
		$i++;
	};
	
	if(empty($str))	return [];
	
	return ['spr'=>$str[0], 'seq'=>$i];
}
/**
 * 创建数据库操作对象
 *
 *  @param	string	$tableName	表名
 *  @return	object	数据库连接对象
 */
function D(string $tableName='_dbDriver'):object
{
	$app='';
	if(strstr($tableName, '://')){
		//跨项目调用
		list($app, $tableName) = explode('://',$tableName);
	}
	
	$db = \Candy\Core\Container::getInstance()->get($tableName.$app);
	if(is_null($db)){
		$db = N('DBServer')::instance(G('DRIVER'));
		if($tableName != '_dbDriver'){
			$db->setTable(N('Str')::snake($tableName));
			if($app==''){
				$db->path = C('APP_PATH');
			}else{
				$db->path = CANDYROOT . 'Application/' . ucfirst(strtolower($app)).'/';
			}
		}
		\Candy\Core\Container::getInstance()->bind($tableName.$app, $db);
	}
	
	return $db;
}

/**
 * 关闭调试模式
 * @param	bool	$falg	调式模式的关闭开关
 */
function debug(int $falg = 0): void
{
	$GLOBALS['debug'] = $falg;
}

/**
 * 删除IP黑、白名单
 */
function delIPList(string $type = 'b', string $ip = ''):bool
{
	$data['type'] = $type;
	$exp_time > 0 && $data['exp_time'] = $exp_time;
	if(is_array($ip)){
		[$data['ip_start'], $data['ip_end']] = $ip;
	}else{
		$data['ip_start'] = $ip;
	}
	return \Candy\Core\Security::delete($data);
}

/**
 *遍历目录
 *
 *@param 目录路径
 *@return array 遍历目录的数组
 */
function dirs(string $dir,bool $fullpath = false): array
{
	$dir = rtrim($dir, '/');
	$dirs = [];
	foreach(glob($dir.'/*') as $tfile){
		if(is_dir($tfile)){
			$dirs[]	= $fullpath ? $tfile . '/' : str_replace($dir.'/', '',$tfile);
		}
	}
	sort($dirs);
	return $dirs;
}

/**
 * 重写define方法
 * @param	bool	$falg	调式模式的关闭开关
 */
function doDefine(string $key,mixed $value): void
{
	defined($key) OR define($key, $value);
}

/**
 * 重写define方法
 * @param	bool	$falg	调式模式的关闭开关
 */
function dump(...$args): void
{
	p($args);
	stop();
}

/**
 * inc 配置信息修改
 * 
 * @param $name 配置名称
 */
function editInc(string $name = '',array $data = []): bool
{
	if(empty($name) || empty($data)) return false;

	//配置路径
	$configPath = INCPATH . $name . '.inc.php';
	$config = is_file($configPath) ? include($configPath) : [];
	
	if(empty($data)) return false;
	
	//修改配置
	file_put_contents($configPath, '<?php'. PHP_EOL .'defined(\'CANDY\') OR die(\'You Are A Bad Guy. o_O???\');'. PHP_EOL .'return ' . var_export(array_merge($config, $data), true) . ';' .PHP_EOL);
	
	return true;
}

/**
 * 快速文件数据读取和保存 针对简单类型数据 字符串、数组
 *
 * @param $name 缓存名称
 * @param mixed $value 缓存值
 * @param $expires_time 超时时间
 * @return mixed
 */
function F(string $name,mixed $value='',int $expires_time = 3600):mixed
{
    static $_cache  = [];
	
	$cache = \Candy\Extend\Cache\CacheServer::instance();
	
	if(explode('\\', $cache::class)[4] != 'File'){
		$cache = $cache->getMem();
	}
	
    if ('' !== $value){
        if (is_null($value)){
            return $cache->delete($name);
        } else {
            $_cache[$name] = $value;
			$cache->set($name, $value, $expires_time);
        }
    }
    if (isset($_cache[$name])){
		return $_cache[$name];
	}else{
		// 获取缓存数据
		$value = $cache->get($name);
		if($value){
			$_cache[$name] = $value;
		}
	}
    return $value;
}

/**
 * 文件下载
 *
 * @param $filename 文件地址
 * @return boolean
 */
function fileDownload(string $file,string $rename = '')
{
	if(headers_sent()) return false;
	if(!$file) return false;
	if (empty($rename)) {
        if(strpos($file, '/') === false && strpos($file, '\\') === false){
            $filename = $file;
        }else{
            $filename = basename($file);
        }
    } else {
        $filename = $rename;
    }
	
	headerLoading('Content-Description: File Transfer');
    headerLoading('Content-Type: application/octet-stream');
    headerLoading('Content-Disposition: attachment; filename="' . $filename .'"');
    headerLoading('Expires: 0');
    headerLoading('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    headerLoading('Pragma: public');
    headerLoading('Content-Length: ' . filesize($file));
    ob_clean();
    flush();
    fileRead($file);
}

/**
 * 区分大小写的文件存在判断
 *
 * @param $filename 文件地址
 * @return boolean
 */
function fileExistsCase(string $filename): bool
{
	if(is_file($filename)){
		if(basename(realpath($filename)) != basename($filename))
			return false;
		return true;
	}
	return false;
}

/**
 * 获取文件内容 支持代码解密
 *
 * @param $file 文件路径
 * @return  $contents 文件内容
 */
function fileGetContents(string $file): string
{
	$contents = file_get_contents($file);
	//包含密文
	if(stristr($contents, '-----BEGIN CIPHERTEXT-----') !== false){
		$contents = transCode($contents);
	}
	return $contents;
}

/**
 * 储存文件内容 支持代码加密
 *
 * @param $file 文件路径
 * @param $content 文件内容
 */
function filePutContents(string $file,string $content): void
{
	//创建文件夹
	\Candy\Extend\Dir::create(dirname($file));
	
	file_put_contents($file, $content);
}

/**
 * 读取文件
 *
 *@return  $filename 文件路径
 *@return boolean $deal 换行码处理
 */
function fileRead(string $filename,bool $deal = false): string
{
	$contents = false;
	if(file_exists($filename) && is_readable($filename) && ($fd = @fopen($filename, 'rb'))){
        while(!feof($fd)){
            $contents .= fread($fd, 8192);
        }
        fclose($fd);
		
		$contents = mConvtEncod($contents);
		
		if($deal){
			//统一换行符
			$contents = preg_replace('/\n|\r\n|\r/', PHP_EOL,$contents);
			$lines = explode(PHP_EOL, $contents);
			$contents = '';
			foreach($lines as $line){
				if(trim($line) != ''){
					$contents .= '<p>'.$line.'</p>';
				}
			}
		}
	}
	
	return $contents;
}

/**
 * 写入文件
 *
 *@return  $filename 文件路径
 *@return boolean $contents 写入内容
 */
function fileWrite(string $filename,string $contents): bool
{
	\Candy\Extend\Dir::create(dirname($filename));
	if(!($fd = @fopen($filename, 'wb'))){
        $_tmp_file = $filename . DIRECTORY_SEPARATOR . uniqid('wrt');
    	if(!($fd = @fopen($_tmp_file, 'wb'))){
			return false;
    	}
	}
    fwrite($fd, $contents);
   	fclose($fd);
	@chmod($filename, 0755);
	return true;
}

/**
 * 查询函数方法位置
 *
 * @param  string $funcname
 * @return string 
 */
function functionDump($funcname): string
{
	try {
		if(is_array($funcname)) {
			$func = new \ReflectionMethod($funcname[0], $funcname[1]);
			$funcname = $funcname[1];
		} else {
			$func = new \ReflectionFunction($funcname);
		}
	} catch (\ReflectionException $e) {
		return $e->getMessage();
	}
	$start = $func->getStartLine();
	$end =  $func->getEndLine();
	$filename = $func->getFileName();
	return "function $funcname defined by $filename($start - $end)\n";
}

/**
 * 获取和设置配置参数 支持批量定义 S函数扩展  workerman模式进程间共享变量
 *
 * @param string|array $name 配置变量
 * @param mixed $value 配置值
 * @param mixed $default 默认值
 * @return mixed
 */
function G(string|array|null $name = null,mixed $value = '',mixed $default = null):mixed
{
	//cli模式
	if(defined('CLIWORKING')){
		$globaldata = S('c', 'globaldata');
		//进程模式存在
		if(is_null($globaldata)){
			//nothing to do.
		}else{
			$datatmp = $globaldata->data;
			
			// 优先执行设置获取或赋值
			if(is_string($name)){
				if(!strpos($name, '.')){
					$name = strtoupper($name);
					if($value === '')
						return isset($datatmp[$name]) ? $datatmp[$name] : $default;
					
					$datatmp[$name] = $value;
					$globaldata->data = $datatmp;
					return null;
				}
				// 二维数组设置和获取支持
				$name = explode('.', $name);
				$name[0]   =  strtoupper($name[0]);
				if($value === '')
					return isset($datatmp[$name[0]][$name[1]]) ? $datatmp[$name[0]][$name[1]] : $default;
				
				$datatmp[$name[0]][$name[1]] = $value;
				$globaldata->data = $datatmp;
				return null;
			}
			
			// 批量设置
			if (is_array($name)){
				$datatmp = array_merge($datatmp, array_change_key_case($name,CASE_UPPER));
				$globaldata->data = $datatmp;
				return null;
			}
		}
	}
	
	return S('g', $name, $value, $default);
}

/**
 *获取APP目录
 *
 *@param $appname 项目名称
 *@return mixed 路径或者false
 */
function getAppPath(string $appName = '',string $type = null):string|bool
{
	if(empty($appName))
		return C('APP_PATH');
	
	if($type == 'addon'){
		return CANDYROOT.'Addons/'. $appName .'/';
	}else{
		//判断是否存在 不存在则默认Home
		$appPath = CANDYROOT.'Application/'. $appName .'/';
		if(is_dir($appPath)){
			return $appPath;
		}else{
			//不存在操作检查是否为Addon
			$is_addon = \Candy\Core\Addon::checkAddon($appName);
			if($is_addon){
				return CANDYROOT.'Addons/'. $appName .'/';
			}
		}
	}
	
	return false;
}

/**
 * 获取解码
 */
function getDecode(string $code,int $start,int $number): array
{
	$authcode = [];
	$code = str_replace('-', '', $code);
	for($i=1;$i<=$number;$i++){
		if($i<5){
			$key = '';
			for($z=0;$z<4;$z++){
				$t = $start + $z*4;
				if($t < 16 && $t+$i <= 16){
					$key .= substr($code, $t, $i);
				}else{
					if($t > 16){
						$key .= substr($code, $t-15, $i);
					}else{
						$key .= substr($code, $t, 16-$t);
						$key = substr($code, 0, $t+$i-16) . $key;
					}
				}
			}
		}
		$authcode[] = $key;
	}
	
	return $authcode;
}

/**
 * 获取IP列表
 */
function getIPList(string $type = 'b'):array
{
	return \Candy\Core\Security::getAll($type);
}

/**
 * 获取服务器ip
 */
function getLocalIP(string $host = ''):string
{
    static $localip = '';
	if(empty($localip)){
		$url = 'http://pv.sohu.com/cityjson?ie=utf-8';
		$m_str = @file_get_contents($url);
		if(empty($m_str)){
			empty($host) && $host = $_SERVER['SERVER_ADDR'];
			$localip = gethostbyname($host);
		}else{
			$localip = json_decode(str_replace(['var returnCitySN = ',';'], '', $m_str), true)['cip'];
		}
	}
	return $localip;
}

/**
 * 获取操作名称
 */
function getOperate(string $name = ''):string
{
	return N('Node')::full($name);
}

/**
 *获取拼音
 *
 *@param string $str 字符串
 *@param bool $onlyfirst 获取首字母
 *@param string $delimiter 分隔符
 *@param bool $ucfirst 首字母大写
 *@param string $charset 编码
 *@return  拼音字符串
 */
function getPinyin(string $str = '',bool $onlyfirst = false,string $delimiter = '',bool $ucfirst = false,string $charset = 'utf-8'): string
{
	if(empty($str)) return '';
	return \Candy\Extend\Str\Pinyin::get($str, $onlyfirst, $delimiter, $ucfirst, $charset);
}

/**
 *生成随机字符串
 *
 *@param int $num 字符串长度
 *@param int $type 字符串类型 0数字 1数字+小写字母 2数字+字母 3小写字母 4 字母 5 大写字母
 *@return  生成的字符串
 */
function getRandomStr(int $num = 4,int $type = 0): string
{
	return \Candy\Extend\Str\Str::random($num, $type);
}

/**
 * time()方法重写网络时间戳
 *
 * @return integer 时间戳
 */
function gmTime(): int
{
	if(defined('NETWORKTIME')){
		$api = 'http://api.m.taobao.com/rest/api3.do?api=mtop.common.getTimestamp'; 	//淘宝接口
		$json = @file_get_contents($api); 
		$arr = json($json,'un');
		if(empty($arr['data']['t'])){
			//失败调用
			$api = 'http://quan.suning.com/getSysTime.do'; 	//苏宁接口
			$json = @file_get_contents($api); 
			$arr = json($json,'un');
			if(empty($arr['sysTime2'])){
				return strtotime($arr['sysTime2']);
			}else{
				return time();
			}
		}else{
			return $arr['data']['t'];
		}
	}else{
		return time();
	}
}

function halt(...$args): void
{
	P(...$args);
	stop();
}

/**
 * header兼容跳转
 *
 * @param $info 跳转内容
 */
function headerLoading(string $info,int $code = 0,string $body = ''): void
{
	if(defined('CLIWORKING')){
		$response = \Candy\Core\Container::getInstance()->get('Response');
		
		if(empty($info)){
			if($code > 0){
				if(empty($body)){
					$response->withStatus($code);
				}else{
					$response->withStatus($code)->withBody($body);
				}
			}
		}else{
			if(is_int(stripos(strtolower($info), 'location'))){
				[$location, $info] = explode(':', $info, 2);
				$code = 302;
			}
			if($code > 0){
				if($code == 302){
					$response->withStatus(302);
					$response->header('location', trim($info));
				}else{
					if(empty($body)){
						$response->withStatus($code, $info);
					}else{
						$response->withStatus($code, $info)->withBody($body);
					}
				}
			}else{
				[$name, $info] = explode(':', $info, 2);
				$response->header(trim($name), trim($info));
			}			
		}
	}else{
		//返回状态不是跳转
		if($code > 0){
			$info = 'HTTP/1.1 '. $code .' ' . $info;
		}else{
			header($info);
		}
		if(!empty($body))
			header($body);
	}
}

/**
 * 去掉HTML代码中的HTML标签，返回纯文本
 *
 * @param $document 待处理的字符串
 */
function html2txt(string $document): string
{
	$text = preg_replace(["'<script[^>]*?>.*?</script>'si", "'<[\/\!]*?[^<>]*?>'si", "'#[^>]*?{.*?}'si", "'.[^>]*?{.*?}'si", "'([\r\n])[\s]+'", "' 'si", "'&(quot|#34);'i", "'&(amp|#38);'i", "'&(lt|#60);'i", "'&(gt|#62);'i", "'&(nbsp|#160);'i", "'&(iexcl|#161);'i", "'&(cent|#162);'i", "'&(pound|#163);'i", "'&(copy|#169);'i"], ["", "", "", "", "", "", "\"", "&", "<", ">", " ", chr(161), chr(162), chr(163), chr(169)], $document);
	return $text;
}

/**
 * Hex转字符串
 * 
 * @param $hex 字符串
 * @return  
 */
function hex2str(string $hex): string
{
	$string='';
	for ($i=0; $i < strlen($hex)-1; $i+=2){
		$string .= chr(hexdec($hex[$i].$hex[$i+1]));
	}
	return $string;
}

/**
 * 获取输入参数 支持过滤和默认值
 *
 * @param $name 变量的名称 支持指定类型
 * @param mixed $default 不存在的时候默认值
 * @param mixed $filter 参数过滤方法
 * @param mixed $datas 要获取的额外数据源
 * @return mixed
 */
function I(string $name,mixed $default = '',mixed $filter = null,mixed $datas = null):mixed
{
	static $_PUT = null;
	if(strpos($name,'/')){ // 指定修饰符
		[$name, $type] = explode('/', $name, 2);
	}elseif(C('VAR_AUTO_STRING')){ // 默认强制转换为字符串
        $type = 's';
    }
    if(strpos($name,'.')){ // 指定参数来源
        [$method, $name] = explode('.',$name,2);
    }else{ // 默认为自动判断
        $method = $name ?: 'param';
		$name = '';
    }
	
    switch(strtolower($method)){
        case 'get':   
        	$input = &$_GET;
        	break;
        case 'post':   
        	$input = &$_POST;
        	break;
        case 'put':   
        	if(is_null($_PUT)){
            	parse_str(file_get_contents('php://input'), $_PUT);
        	}
        	$input = $_PUT;        
        	break;
        case 'param':
            switch($_SERVER['REQUEST_METHOD']){
                case 'POST':
                    $input = $_POST;
                    break;
                case 'PUT':
                	if(is_null($_PUT)){
                    	parse_str(file_get_contents('php://input'), $_PUT);
                	}
                	$input = $_PUT;
                    break;
                default:
                    $input = $_GET;
            }
            break;
        case 'path':   
            $input = [];
            if(!empty($_SERVER['PATH_INFO'])){
                $depr = C('URL_PATHINFO_DEPR');
                $input = explode($depr,trim($_SERVER['PATH_INFO'],$depr));         
            }
            break;
        case 'request':
        	$input = &$_REQUEST;   
        	break;
        case 'session':
			if(defined('CLIWORKING')){
				$input = session(); 
			}else{
				$input = &$_SESSION; 
			}
        	break;
        case 'cookie':   
        	$input = &$_COOKIE;    
        	break;
        case 'server':   
        	$input = &$_SERVER;    
        	break;
        case 'data':   
        	$input = &$datas;      
        	break;
        case 'files':   
        	if(defined('CLIWORKING')){
				$Request = Container::getInstance()->get('Request');
				$_FILES = $Request->file();
			}  
			if(''==$name){
				return $_FILES;
			}else{
				return $_FILES[$name];
			}
        	break;
        default:
            return null;
    }
	
    if(''==$name){ // 获取全部变量
        $data = $input;
        $filters = isset($filter) ? $filter : C('DEFAULT_FILTER');
        if($filters){
            if(is_string($filters)){
                $filters = explode(',',$filters);
            }
            foreach($filters as $filter){
                $data = \Candy\Extend\Arr::arrayMapRecursive($filter,$data); // 参数过滤
            }
        }
    }elseif(isset($input[$name])){ // 取值操作
        $data = $input[$name];
        $filters = isset($filter) ? $filter : C('DEFAULT_FILTER');
        if($filters){
            if(is_string($filters)){
                if(0 === strpos($filters,'/')){
                    if(1 !== preg_match($filters,(string)$data)){
                        // 支持正则验证
                        return isset($default) ? $default : null;
                    }
                }else{
                    $filters = explode(',',$filters);                    
                }
            }elseif(is_int($filters)){
                $filters = [$filters];
            }
            
            if(is_array($filters)){
                foreach($filters as $filter){
                    if(function_exists($filter)){
                        $data = is_array($data) ? \Candy\Extend\Arr::arrayMapRecursive($filter,$data) : $filter($data); // 参数过滤
                    }else{
                        $data = filter_var($data,is_int($filter) ? $filter : filter_id($filter));
                        if(false === $data){
                            return isset($default) ? $default : null;
                        }
                    }
                }
            }
        }
        if(!empty($type)){
        	switch(strtolower($type)){
        		case 'a':	// 数组
        			$data =	(array)$data;
        			break;
        		case 'd':	// 数字
        			$data =	(int)$data;
        			break;
        		case 'f':	// 浮点
        			$data =	(float)$data;
        			break;
        		case 'b':	// 布尔
        			$data =	(boolean)$data;
        			break;
                case 's':   // 字符串
                default:
                    $data = (string)$data;
        	}
        }
    }else{ // 变量默认值
        $data = isset($default) ? $default : null;
    }
    is_array($data) && array_walk_recursive($data,'stripslashesDeep');
    return $data;
}

/**
 * 引入非规则扩展类库
 */
function import(string $path,string $startpath = '',string $suffix = '.php'): bool
{
	//基础路径转换
	$path = explode('.', $path);
	
	//本项目调用
	if($path[0] == '@'){
		array_shift($path);
		$m = strtolower($path[0]);
		array_shift($path);
		if($m == 'app'){
			$path = RUNTIME. 'Controls/' . C('APPNAME') . '/' . strtolower($path[0]) . 'action.class.php';
		}else{
			$path = RUNTIME. 'Models/' . C('APPNAME'). '/' . strtolower($path[0]) . 'model.class.php';
		}
		
		return requireCache($path);
	}
	
	//默认开始路径
	empty($startpath) && $startpath = SOURCEPATH . 'Vendor/';
	
	//文件路径
	$path = $startpath . implode('/', $path) . $suffix;
	
	return requireCache($path);
}

/**
 * 引入语言库
 */
function importLang(array $path = []): void
{
	if(!empty($path)){
		foreach($path as $lang){
			[$appName, $action] = explode('/', $lang, 2);
			$file = CANDYROOT .'Application/'. $appName .'/Languages/'. G('LANG') .'/'. $action .'.lang.php';
			if(fileExistsCase($file))
				include($file);
		}
		C('LANGINFO', $LANG);
	}
}

/**
 * 优化的include
 *
 * @param $filename 文件地址
 * @return boolean
 */
function includeCache(string $filename,bool $code = false): bool
{
	static $_importFiles = [];
	if (!isset($_importFiles[$filename])){
		if (fileExistsCase($filename)){
			if($code){
				$classContent=fileGetContents($filename);
				include 'code://'.$classContent;
			}else{
				include $filename;
			}
			$_importFiles[$filename] = true;
		} else {
			$_importFiles[$filename] = false;
		}
	}
	return $_importFiles[$filename];
}

/**
 * 初始化模版路径
 */
function initTplPath(string $appname = ''): void
{
	empty($appname) && $appname = C('APPNAME');
	//初始化路径
	C(['TPL_PATH'=>null, 'TEMPLATE_DIR'=>null]);
	
	//分解模板路径
	$appTplPath = C('APP_TPL_PATH');
	if(!is_null($appTplPath)){
		//多项目自定义目录
		if(is_array($appTplPath)){
			foreach($appTplPath as $tplPath){
				list($app, $path) = explode('@', $tplPath);
				if($app == $appname){
					C('TPL_PATH', trim(trim($path, './'),'/'));
					break;
				}
			}
		}else{
			list($app, $path) = explode('@', $appTplPath);
			if($app == $appname){
				C('TPL_PATH', trim(trim($path, './'),'/'));
			}
		}
	}
	
	//添加模版目录判断
	$appname = preg_replace('/(.)(?=[A-Z])/u', '$1/', $appname);
	$tpl_path = C('TPL_PATH');
	$tplPath = is_null($tpl_path) ? CANDYROOT . 'Application/' . $appname .'/Views/'. C('TPLSTYLE') : CANDYROOT. $tpl_path .'/'. C('TPLSTYLE');
	if(!is_dir($tplPath)){
		$tplPath = dirname($tplPath);
		C('TPLSTYLE', '');
	}
	
	//浏览模式
	if(isMobile()){
		//手机模式
		$lastpath = is_dir($tplPath . '/mobile') ? '/mobile' : (is_dir($tplPath . '/pc') ? '/pc' : '');
	}else{
		$lastpath = is_dir($tplPath . '/pc') ? '/pc' : '';
	}
	C('LASTPATH', $lastpath);
	
	//模板资源路径
	$b_res = 'c_res_'. $appname;
	if(!empty($$b_res)){
		C('C_RES', $$b_res);
		\Candy\Core\Debug::addMsg('[<b>APP</b>] 当前应用( <b>'. $appname .'</b> )的资源请求位置：'. C('C_RES'), 3);
	}else {
		if(is_null(C('ISADDON'))){
			C('TEMPLATE_DIR', $tplPath . $lastpath);
			$GLOBALS['res']= str_replace(CANDYROOT, '/', C('TEMPLATE_DIR')) .'/Resource/'; 
			C('COMPOLE_DIR', CANDYROOT .'Runtime/Comps/'. $appname .'/'. C('TPLSTYLE') . $lastpath);
			C('CACHE_DIR', CANDYROOT .'Runtime/Cache/'. $appname .'/'. C('TPLSTYLE') . $lastpath);
			
			//当前应用模板的资源
			C('C_RES', rtrim($GLOBALS['res'], '/'));
			\Candy\Core\Debug::addMsg('[<b>APP</b>] 当前应用( <b>'. $appname .'</b> )的资源请求位置（默认）：'. C('C_RES'), 3);
		}else{
			//插件模式
			C('TEMPLATE_DIR', CANDYROOT .'Addons/'. $appname .'/Views' .  C('LASTPATH'));
			$GLOBALS['res']= str_replace(CANDYROOT, '/', C('TEMPLATE_DIR')) .'/Resource/'; 
			
			C('COMPOLE_DIR', CANDYROOT .'Runtime/Comps/Addons/'. $appname .  C('LASTPATH'));
			//创建缓存目录
			if(!is_dir(C('COMPOLE_DIR')))
				\Candy\Extend\Dir::create(C('COMPOLE_DIR'));
			
			C('CACHE_DIR', CANDYROOT .'Runtime/Cache/Addons/'. $appname .  C('LASTPATH'));
			//创建缓存目录
			if(!is_dir(C('CACHE_DIR')))
				\Candy\Extend\Dir::create(C('CACHE_DIR'));
			
			//当前应用模板的资源
			C('C_RES', rtrim($GLOBALS['res'], '/'));
			\Candy\Core\Debug::addMsg('[<b>APP</b>] 当前插件( <b>'. $appname .'</b> )的资源请求位置（默认）：'. C('C_RES'), 3);
		}
	}
}

/**
 * debug模式
 */
function isDebug(): bool
{
	return (defined('DEBUG') && DEBUG == 1 && $GLOBALS['debug'] === 1) ? true : false;
}

/**
 *浏览设备判断
 */
function isMobile(): bool
{
	// 如果有HTTP_X_WAP_PROFILE则一定是移动设备  
	if (isset($_SERVER['HTTP_X_WAP_PROFILE']))  
		return true;  
	
	// 如果有HTTP_PROFILE则一定是移动设备  
	if (isset($_SERVER['HTTP_PROFILE']))  
		return true;  
	  
	//此条摘自TPM智能切换模板引擎，适合TPM开发  
	if(isset ($_SERVER['HTTP_CLIENT']) && 'PhoneClient'==$_SERVER['HTTP_CLIENT'])  
		return true;  
	
	//如果via信息含有wap则一定是移动设备,部分服务商会屏蔽该信息  
	if (isset($_SERVER['HTTP_VIA']))  
		//找不到为flase,否则为true  
		return stristr($_SERVER['HTTP_VIA'], 'wap') ? true : false;  
	//判断手机发送的客户端标志,兼容性有待提高  
	if (isset($_SERVER['HTTP_USER_AGENT'])){  
		$clientkeywords = ['nokia','sony','ericsson','mot','samsung','htc','sgh','lg','sharp','sie-','philips','panasonic','alcatel','lenovo','iphone','ipod','blackberry','meizu','android','netfront','symbian','ucweb','windowsce','palm','operamini','operamobi','openwave','nexusone','cldc','midp','wap','mobile'];  
		//从HTTP_USER_AGENT中查找手机浏览器的关键字  
		if (preg_match("/(" . implode('|', $clientkeywords) . ")/i", strtolower($_SERVER['HTTP_USER_AGENT']))){  
			return true;  
		}  
	}  
	//协议法，因为有可能不准确，放到最后判断  
	if (isset($_SERVER['HTTP_ACCEPT'])){  
		// 如果只支持wml并且不支持html那一定是移动设备  
		// 如果支持wml和html但是wml在html之前则是移动设备  
		if ((strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') !== false) && (strpos($_SERVER['HTTP_ACCEPT'], 'text/html') === false || (strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') < strpos($_SERVER['HTTP_ACCEPT'], 'text/html')))){  
			return true;  
		}  
	}  
	return false;  
}

/**
 * 判断系统
 */
function isWinOS(): bool
{
	return strpos(strtolower(PHP_OS), 'win') === 0 ? true : false;
}

/**
 * 判断是否SSL协议
 *
 * @return boolean
 */
function isSsl(): bool
{
	$httpsopen = G('cfgs.httpsopen');
	if(isset($_SERVER['HTTPS']) && ('1' == $_SERVER['HTTPS'] || 'on' == strtolower($_SERVER['HTTPS']))){
		return true;
	}elseif(isset($_SERVER['SERVER_PORT']) && ('443' == $_SERVER['SERVER_PORT'])){
		return true;
	}elseif($httpsopen == 1){
		return true;
	}
	return false;
}

/**
 * 优化json_encode 和 json_decode
 *
 * @return int|string
 */
function json(mixed $data,string $type='en'):int|string|array
{
	if($type == 'en'){
		if(defined('JSON_UNESCAPED_UNICODE')){
			return str_replace("\\/", '/', json_encode($data, JSON_UNESCAPED_UNICODE));
		}else{
			if($data === null)
				return 'null';
			if($data === true)
				return 'true';
			if($data === false)
				return 'false';
			
			if(is_numeric($data))
				return $data;
			
			if(is_string($data)){
				$text = $data;
				$text = str_replace('\\', '\\\\', $text);
				$text = str_replace(["\r", "\n", "\t", "\""], ['\r', '\n', '\t', '\\"'], $text);
				return '"' . $text . '"';
			}
			
			if(is_array($data) || is_object($data)){
				$arr = [];
				$is_obj = is_object($data) || (array_keys($data) !== range(0, count($data) - 1));
				foreach($data as $k=>$v){
					if($is_obj){
						$arr[] = json($k) . ':' . json($v);
					}else{
						$arr[] = json($v);
					}
				}
				if($is_obj){
					return '{' . join(',', $arr) . '}';
				}else{
					return '[' . join(',', $arr) . ']';
				}
			}
		}
	}else{
		return json_decode($data, true);
	}
}

/**
 * jsonRpc 服务对象
 *
 * @param $serverName 对象名
 * @return array $config 服务地址
 */
function jsonRpc(string $serverName,array $config): object
{
	\JsonRpc\RpcClient::config($config);
	return \JsonRpc\RpcClient::instance($serverName);
}

/**
 * 语言方法
 *
 * @param $key 语言库键
 * @return  语言库值
 */
function L($key=null):array|null|string
{
	$LANG = C('LANGINFO');
	if(is_null($key)){
		//返回所有语言配置
		return $LANG;
	}
	
	return empty($LANG[$key]) ? '' : $LANG[$key];
}

/**
 * 加载配置
 *
 * @param $name 配置名称
 */
function loadConfig(string $name = '',bool $return = true)
{
	static $_loadConfig = [];
	if(empty($name)) return [];
	//配置位置
	$configPath = INCPATH . $name . '.inc.php';
	if($return){
		if(isset($_loadConfig[$name])) return $_loadConfig[$name];
		//配置数据
		$config = is_file($configPath) ? include($configPath) : [];
		$_loadConfig[$name] = $config;
		return $config;
	}else{
		includeCache($configPath);
	}
}

/**
 * 创建Models操作对象
 *
 *  @param	string	$className	类名
 *  @return	object	Models操作对象
 */
function M(string $className = ''):object
{
	if(empty($className))
		\Candy\Core\Debug::showMessage('M()方法必须传参！', 'Mysql');
	
	$app='';
	if(strstr($className, '://')){
		//跨项目调用
		list($app, $className) = explode('://',$className);
	}
	
	$model = \Candy\Core\Container::getInstance()->get($className.$app);
	
	if(is_null($model)){
		//创建模型
		$model = \Candy\Core\App::model($className, $app);
		$model = new $model();
		$model->setTable(N('Str')::snake($className));
		if($app==''){
			$model->path = C('APP_PATH');
		}else{
			$model->path = CANDYROOT . 'Application/' . strtolower($app).'/';
		}
		\Candy\Core\Container::getInstance()->bind($className.$app, $model);
	}
	
	return $model;
}

/**
 * 自动检测编码并转码
 * 
 * @param $str 需要转换的字符串
 * @param $charset 转换格式
 * @return  
 */
function mConvtEncod(string $str,string $charset = 'UTF-8'): string
{
	$encode = mb_detect_encoding($str, ["ASCII",'UTF-8',"GB2312","GBK",'BIG5']);
	return mb_convert_encoding($str, $charset, $encode);
}

/**
 * 压缩zip
 */
function mkZip(string $path,array $files,string $root = ''): bool
{
	if(empty($files) || empty($path)) return false;
	
	//zip对象
	$zip = N('ZipArchive');
	//创建目录
	N('Dir')::create(dirname($path));
	//打开压缩包,没有则创建
	if ($zip->open($path, \ZIPARCHIVE::CREATE) === true){
		//压缩方式
		if(empty($root)){
			foreach($files as $file){
				$zip->addFile($file['path'], basename($file['path'])); 
			}
		}else{
			//添加到压缩包
			foreach($files as $file){
				$zip->addFile($file['path'], str_replace($root, '', $file['path'])); 
			}
		}
		return $zip->close();
	}else{
		return false;
	}
}

/**
 * 字符串截取，支持中文和其他编码
 * 
 * @param $str 需要转换的字符串
 * @param $start 开始位置
 * @param $length 截取长度
 * @param $charset 编码格式
 * @param $suffix 截断显示字符
 */
function mSubStr(string $str,int $length,string $suffix = '...',int $start = 0,string $charset = 'utf-8'): string
{
	$str = str_replace([' ','&nbsp;', '&amp;', '&quot;', '&#039;', '&ldquo;', '&rdquo;', '&mdash;', '&lt;', '&gt;', '&middot;', '&hellip;'], ['∵',' ', '&', '"', "'", '“', '”', '—', '<', '>', '·', '…'], $str);
	if (function_exists('mb_substr')){
		$i_str_len = mb_strlen($str);
		$s_sub_str = mb_substr($str, $start, $length, $charset);
		if ($length >= $i_str_len){
			return $s_sub_str;
		} 
		return $s_sub_str . $suffix;
	} elseif (function_exists('iconv_substr')){
		return iconv_substr($str, $start, $length, $charset);
	} 
	$re['utf-8'] = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}/";
	$re['gb2312'] = "/[\x01-\x7f]|[\xb0-\xf7][\xa0-\xfe]/";
	$re['gbk'] = "/[\x01-\x7f]|[\x81-\xfe][\x40-\xfe]/";
	$re['big5'] = "/[\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|\xa1-\xfe])/";
	preg_match_all($re[$charset], $str, $match);
	$slice = join("", array_slice($match[0], $start, $length));
	$slice = str_replace(['∵', '&', '"', "'", '“', '”', '—', '<', '>', '·', '…'], [' ', '&amp;', '&quot;', '&#039;', '&ldquo;', '&rdquo;', '&mdash;', '&lt;', '&gt;', '&middot;', '&hellip;'], $slice);
	return $slice . $suffix;
}

/**
 * 获取对象
 *
 * @param $ClassName 对象名
 * @return  对象或者静态对象名
 */
function N(string $className = '',array $vars = [],bool $newInstance = false,array $invokeFunction = []):object|string
{
	if(empty($className)){
		\Candy\Core\Debug::showMessage('N()方法，必须指类名！', 'PHP');
	}
	return \Candy\Core\Container::getInstance()->make($className, $vars, $newInstance, $invokeFunction);
}
 
/**
 * 将obj深度转化成array
 * 
 * @param  $obj 要转换的数据 可能是数组 也可能是个对象 还可能是一般数据类型
 * @return array || 一般数据类型
 */
function objToArray($obj):array
{
	if(is_array($obj)){
		foreach($obj as &$value){
			$value = objToArray($value);
		} 
		return $obj;
	}elseif(is_object($obj)){
		$obj = get_object_vars($obj);
		return objToArray($obj);
	}else{
		return $obj;
	} 
}

/**
 * 输出各种类型的数据，调试程序时打印数据使用。
 *
 * @param	mixed	参数：可以是一个或多个任意变量或值
 */
function P(...$args): void
{
	if(count($args)<1){
		\Candy\Core\Debug::addMsg('<font color=\'red\'>必须为p()函数提供参数!');
		return;
	}
	ob_start();
	$style = '<style>
	pre#debug{margin:10px;font-size:14px;color:#222;font-family:Consolas ;line-height:1.2em;background:#f6f6f6;border-left:5px solid #444;padding:5px;width:95%;word-break:break-all;}
	pre#debug b{font-weight:400;}
	#debug #debug_str{color:#E75B22;}
	#debug #debug_keywords{font-weight:800;color:00f;}
	#debug #debug_tag1{color:#22f;}
	#debug #debug_tag2{color:#f33;font-weight:800;}
	#debug #debug_var{color:#33f;}
	#debug #debug_var_str{color:#f00;}
	#debug #debug_set{color:#0C9CAE;}</style>';
	foreach($args as $arg){
		if (is_array($arg) || is_object($arg)){
			print_r($arg).'<br>';
		} else if (is_resource($arg)){
			echo (string)$arg.'<br>';
		} else {
			echo var_dump($arg).'<br>';
		} 
	}
	$out = ob_get_clean(); //缓冲输出给$out 变量	
	$out = preg_replace('/"(.*)"/', '<b id="debug_var_str">"' . '\\1' . '"</b>', $out); //高亮字符串变量
	$out = preg_replace('/=\>(.*)/', '=>' . '<b id="debug_str">' . '\\1' . '</b>', $out); //高亮=>后面的值
	$out = preg_replace('/\[(.*)\]/', '<b id="debug_tag1">[</b><b id="debug_var">' . '\\1' . '</b><b id="debug_tag1">]</b>', $out); //高亮变量
	$out = str_replace(['    ', '(', ')', '=>'], ['  ', '<b id="debug_tag2">(</i>', '<b id="debug_tag2">)</b>', '<b id="debug_set">=></b>'], $out);
	$keywords = ['Array', 'int', 'string', 'object', 'null']; //关键字高亮
	$keywords_to = $keywords;
	foreach($keywords as $key => $val){
		$keywords_to[$key] = '<b id="debug_keywords">' . $val . '</b>';
	} 
	$out = str_replace($keywords, $keywords_to, $out);
	$out = str_replace("\n\n", "\n", $out);
	echo $style . '<pre id="debug">' . $out . '</pre>';
} 

/**
 * inc 配置信息读取
 * 
 * @param $name 配置名称
 */
function readInc(string $name = ''): array
{
	if(empty($name)) return [];
	
	//配置路径
	$configPath = INCPATH . $name . '.inc.php';
	$config = is_file($configPath) ? include($configPath) : [];
	return $config;
}

/**
 * log读取
 * 
 * @param $path 记录路径
 */
function readLog(string $path = null,bool $display=false):string
{
	return \Candy\Core\Log::read($path, $display);
}

/**
 * 优化的require_once
 *
 * @param $filename 文件地址
 * @return boolean
 */
function requireCache(string $filename = '',bool $code=false):string|array
{
	static $_importFiles = [];
	if(empty($filename)) return $_importFiles;
	if(isset($_importFiles[$filename])) return isset($_importFiles[$filename]);
	if (fileExistsCase($filename)){
		if($code){
			$classContent = fileGetContents($filename);
			require 'code://'.$classContent;
		}else{
			require $filename;
		}
		$_importFiles[$filename] = true;
	} else {
		$_importFiles[$filename] = false;
	}
	return $_importFiles[$filename];
}

/**
 * 缓存数据
 */
function S(string $type = 'c',mixed $name = null,mixed $value = '',mixed $default = null,bool $sort = false):mixed
{
	static $_config = [];

	// 无参数时获取所有
	if (empty($name)){
		if($value === ''){
			return $_config[$type];
		}
	}
	
	// 优先执行设置获取或赋值
	if(is_string($name)){
		if(!strpos($name, '.')){
			$name = strtoupper($name);
			if($value === '')
				return isset($_config[$type][$name]) ? $_config[$type][$name] : $default;
			
			$_config[$type][$name] = $value;
			return null;
		}
		// 二维数组设置和获取支持
		$name = explode('.', $name);
		$name[0]   =  strtoupper($name[0]);
		if($value === '')
			return isset($_config[$type][$name[0]][$name[1]]) ? $_config[$type][$name[0]][$name[1]] : $default;
		
		$_config[$type][$name[0]][$name[1]] = $value;
		return null;
	}
	
	// 批量设置
	if (is_array($name)){
		$arr = array_change_key_case($name, CASE_UPPER);
		empty($_config[$type]) && $_config[$type] = [];
		$_config[$type] = $sort ? array_merge($arr, $_config[$type]) : array_merge($_config[$type], $arr);
		return null;
	}
	
	return null; // 避免非法参数
}

/**
 * 安全过滤函数
 *
 * @param $string
 * @return 
 */
function safeReplace(string $string): string
{
	return str_replace(['%20','%27','%2527','*','"',"'",';','<','>',"{",'}','\\'],['','','','','','','','&lt;','&gt;','','',''],$string);
}	

/**
 * session别名
 *
 * @return int|string
 */
function session(string $key='',mixed $value='')
{
	//session对象
	$seobj = null;
	if(defined('CLIWORKING')){
		$seobj = \Candy\Core\Container::getInstance()->get('Request')->session();
	}
	
	$keys = [];
    if(empty($key))
		return is_null($seobj) ? $_SESSION : $seobj->all();
	
	if($value === ''){ //获取值
		if(stripos($key, '.'))
			$keys = explode('.', $key);
		
		if(count($keys) > 0){
			$tmp = is_null($seobj) ? $_SESSION : $seobj->all();
			do{
				$key = array_shift($keys);
				if(isset($tmp[$key])){
					$tmp=$tmp[$key];
				}else{
					return ;
				}
			}while(count($keys) > 0);
			return $tmp;
		}else{
			if(is_null($seobj)){
				if(isset($_SESSION[$key]))
					return $_SESSION[$key];
			}else{
				return $seobj->get($key);
			}
		}
		return null;
	}else{
		if(is_null($value)){
			if(is_null($seobj)){
				unset($_SESSION[$key]);
			}else{
				$seobj->forget($key);
			}
		}else{
			if(is_null($seobj)){
				$_SESSION[$key] = $value;
			}else{
				$seobj->set($key, $value);
			}
		}
	}
}
 
/**
 * 展示调试信息
 */
function showDebugMsg(): void
{
	//框架析构函数
	__destructor();
	
	//debug信息
	if(isDebug() && is_null(C('API'))){
		\Candy\Core\Debug::message();
	}
}
 
/**
 * 展示调试信息
 */
function showTplMsg(string $name = 'debug', string $msg = '', $detailed = '', $type = '',bool $die = false): void
{
	$path = CANDYPATH . 'Tpl/' . $name . '.tpl.php';
	file_exists($path) && include($path);
	stop();
}

/**
 * 文件尺寸转换，将大小将字节转为各种单位大小
 *
 * @param	int	$bytes	字节大小
 * @return	string	转换后带单位的大小
 */
function sizeCount(int $bytes): string
{
	$unit = ['Byte','KB','MB','GB','TB','PB']; 
	return round($bytes/pow(1024,($i=floor(log($bytes,1024)))),2).' '.$unit[$i];
}

/**
 * exit() 方法重写
 */
function stop(string $msg = ' '): void
{
	if(defined('CLIWORKING')){
		echo $msg;
		throw new \Exception('jump_exit');
	}else{
		exit($msg);
	}
}

/**
 * 字符串转Hex
 * 
 * @param $string 字符串
 * @return  
 */
function str2hex(string $string): string
{
	$hex='';
	for ($i=0; $i < strlen($string); $i++){
		$hex .= dechex(ord($string[$i]));
	}
	return $hex;
}

function stripslashesDeep(mixed $value):mixed
{ 
	$value = is_array($value) ? array_map('stripslashesDeep', $value) : (isset($value) ? stripslashes($value) : null); 
	return $value; 
}

/**
 * 去除代码中的空白和注释
 *
 * @param $content 代码内容
 * @return 
 */
function stripWhitespace(string $content): string
{
	$stripStr = '';
	//分析php源码
	$tokens = token_get_all($content);
	$last_space = false;
	for ($i = 0, $j = count($tokens); $i < $j; $i++){
		if (is_string($tokens[$i])){
			$last_space = false;
			$stripStr .= $tokens[$i];
		} else {
			switch ($tokens[$i][0]){
				//过滤各种PHP注释
				case T_COMMENT:
				case T_DOC_COMMENT:
					break;
				//过滤空格
				case T_WHITESPACE:
					if (!$last_space){
						$stripStr  .= ' ';
						$last_space = true;
					}
					break;
				case T_START_HEREDOC:
					$stripStr .= '<<<RUBIK\n';
					break;
				case T_END_HEREDOC:
					$stripStr .= 'RUBIK;\n';
					for($k = $i+1; $k < $j; $k++){
						if(is_string($tokens[$k]) && $tokens[$k] == ';'){
							$i = $k;
							break;
						} else if($tokens[$k][0] == T_CLOSE_TAG){
							break;
						}
					}
					break;
				default:
					$last_space = false;
					$stripStr  .= $tokens[$i][1];
			}
		}
	}
	return $stripStr;
} 

/**
 * 返回token
 */
function takeToken(bool $input = true,string $name = 'token'): void
{
	$token = \Candy\Core\Validate::creatToken();
	if($input){
		echo '<input type="hidden" name="'. $name .'" value="'. $token .'" />';
	}else{
		echo $token;
	}
}

/**
 * 转码
 */
function transCode(string $content): string
{
	preg_match("/\/\*\*\n-----BEGIN CIPHERTEXT-----\n([\S]+)\n-----END CIPHERTEXT-----\n\*\//", $content, $matches);
	$ciphertext = trim($matches[1]);
	
	//识别码
	$s = substr($ciphertext, -1, 1);
	$ciphertext = substr($ciphertext, 0, -1);
	
	//第一层转码
	$contents = "<?php\n" . \Candy\Extend\Str\Mcrypt::code($ciphertext, 'localhost', 'de');
	
	//授权解码
	if(G('AUTHCODE')){
		preg_match_all('/\/\/CIPHER(.*)/', $contents, $m);
		if(isset($m[1]) && count($m[1]) > 0){
			$lv = count($m[1]);
			//获取等级码
			$code = getDecode(G('AUTHCODE'), $s, $lv);
			for($i=0;$i<$lv;$i++){
				$contents = str_replace($m[0][$i], \Candy\Extend\Str\Mcrypt::code($m[1][$i], $code[$i], 'de'), $contents);
			}
		}
	}
	return $contents;
}

/**
 * URL组装 支持不同URL模式
 *
 * @param string|array $path URL表达式，格式：'[控制器/操作#锚点@域名]?参数1=值1&参数2=值2...',兼容模板U方法
 * @param string|array $vars 传入的参数，支持数组和字符串
 * @param string|boolean $suffix 伪静态后缀，默认为true表示获取配置值
 * @param boolean $domain 是否显示域名
 * @return 
 */
function U(...$params): string
{
	//返回当前URL
	if(empty($params))	return $_SERVER['REQUEST_URI'];
	
	//重置
	$app = C('APP');
	$selfAppName = C('APPNAME');
	$isAddon = C('ISADDON');
	$uri = $_SERVER['REQUEST_URI'];
	$url = '';
	$suffix = '';
	$showurl = false;
	$script = '';
	$vars = [];
	$get = [];
	
	//域名
	$domain = (isSsl() ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'];
	
	//控制器层
	$actions = $params[0];
	
	if(is_array($actions)){
		isset($actions[3]) && $showurl = true;
		isset($actions[2]) && $suffix = $actions[2];
		isset($actions[1]) && $vars = $actions[1];
		$actions = $actions[0];
	}
	
	//参数层
	if(!empty($vars)){
		if(is_string($vars)){
			parse_str($vars, $vars);
		}
	}
	$pvars = isset($params[1]) ? $params[1] : [];
	if(is_string($pvars)){
		parse_str($pvars,$pvars);
	}
	$vars = array_merge($vars, $pvars);
	
	//后缀
	isset($params[2]) && $suffix = $params[2];
	empty($suffix) && $suffix = C('URL_HTML_SUFFIX');
	
	//返回域名
	isset($params[3]) && $showurl = true;
	
	//分隔符
	$urlDepr = C('URL_DEPR');
	
	//操作为空
	if(empty($actions)){
		if(empty($vars)){
			return $_SERVER['REQUEST_URI'];
		}else{
			//当前操作替换参数
			[$path, $args] = explode('?', $_SERVER['REQUEST_URI']);
			$extendArgs = '';
			$protectArgs = is_array(G('protectArgs')) ? G('protectArgs') : [];
			foreach($vars as $k=>$v){
				if(!empty($path))
					if(!(($sps = stripos($path, $urlDepr . $k . $urlDepr)) === false)){
						$start = $sps + strlen($k) + 2;
						//分类出参数值
						$tmp = substr($path, $start);
						[$pv] = explode($urlDepr, $tmp, 2);
						stripos($pv, $suffix) === false OR $pv = str_replace($suffix, '', $pv);
						$path = substr($path, 0, $start) . $v . substr($path, $start + strlen($pv));
						
						continue;
					}
				
				if(!empty($args))
					if(!(($sps = stripos($args, $k . '=')) === false)){
						$start = $sps + strlen($k) + 1;
						//分类出参数值
						$tmp = substr($args, $start);
						[$pv] = explode('&', $tmp, 2);
						$args = substr($args, 0, $start) . $v . substr($args, $start + strlen($pv));
						
						continue;
					}
				
				//新增参数
				if(C('URL_MODEL') == 1 && !in_array($key, $protectArgs)){
					$extendArgs .= $urlDepr . $k . $urlDepr . $v;
				}else{
					$args .= (empty($args) ? '?' : '&') . $k . '=' . $v;
				}
			}
			
			//扩展参数
			if(!empty($extendArgs)){
				if(stripos($path, $suffix) === false){
					if($path == '/'){
						$path .= 'Index' . $urlDepr . 'index' . $extendArgs . $suffix;
					}else{
						$path = explode($urlDepr, str_replace('/', $urlDepr, substr($path, 1, -1)));
						$pathNum = count($path);
						if(count($pathNum) > 2){
							$path[0] == 'Home' && array_shift($path);
							$path = '/' . implode($urlDepr, $path);
						}else{
							$path = '/' . str_replace('/', $urlDepr, 'Index/index');
						}
						$path .= $extendArgs . (($urlDepr == '/' && $pathNum < 3) ? '/' : $suffix);
					}
				}else{
					$tmp = explode('/', str_replace($urlDepr, '/',substr(str_replace($suffix, '', $path), 1)));
					$pathNum = count($tmp);
					if($pathNum < 3){
						if($tmp[0] == 'Home'){
							array_shift($tmp);
						}
						if(count($tmp) < 2){
							if(count($tmp) == 0)
								$tmp[] = 'Index';
							
							$tmp[] = 'index';
						}
						$path = '/' . implode($urlDepr, $tmp);
					}else{
						$tmp[0] == 'Home' && array_shift($tmp);
						$path = '/' . implode($urlDepr, $tmp);
					}
					$path .= $extendArgs . $suffix;
				}
			}
			return $path . (empty($args) ? '' : '?' . $args);
		}
	}
	
	//原网址只是修改后缀
	if($actions[0] === '.'){
		return str_replace(C('URL_HTML_SUFFIX'), $actions, $_SERVER['REQUEST_URI']);
	}
	
	//强制带域名返回
	if($actions[0] === '/' && strlen($actions) == 1){
		return $domain . $_SERVER['REQUEST_URI'];
	}
	
	if($actions[0] === '/' || $showurl || !in_array($_SERVER['SERVER_PORT'], ['80','443'])){
		$actions[0] === '/' && $actions = substr($actions, 1);
		$url = $domain;
	}
	
	//锚点直接返回
	if($actions[0] === '#') return $back . $actions;
	
	//指定域名
	if($actions[0] === '@') return str_replace($_SERVER['HTTP_HOST'], substr($actions, 1), $domain . $uri);
	
	//@http(s):// 处理
	$actions = str_replace(['https:://', 'http://'], ['AA&&BB', 'A&B'], $actions);
	
	if(strpos($actions, '/')){
		//多层操作
		[$appName, $control, $action] = explode('/', $actions, 3);
		if(is_null($action)){
			//两层操作
			$action = $control;
			$control = $appName;
			$appName = $selfAppName;
		}
	}else{
		//单层操作
		$action = $actions;
		$control = C('CONTROL');
		$appName = $selfAppName;
	}
	
	//其他参数 #、@等
	$last = '';
	if(stripos($action, '#'))
		[$action, $last] = explode('#', $action, 2);
	
	$do = '';
	if(stripos($action, '@'))
		[$action, $do] = explode('@', $action, 2);
	
	if(!empty($last) && stripos($last, '@'))
		[$last, $do] = explode('@', $last, 2);
	
	//后缀
	if(stripos($action, '.')){
		[$action, $suffix] = explode('.', $action, 2);
		$suffix = '.' . $suffix;
	}
	
	//固定参数
	is_null(G('default.reset')) OR $url = str_replace(G('default.reset'), 'Home', $url);
	if((is_null($app) || $isAddon) && $appName != 'Home' && (is_null(G('default.reset')) || G('default.reset') != $appName)){
		if(str_starts_with($appName, 'Home'))
			$appName = str_replace('Home', '', $appName);
		
		$get['g'] = ucfirst($appName);
	}
	$get['m'] = ucfirst(strtolower($control));
	$get['a'] = strtolower($action);
	
	//合并参数
	$get = array_merge($get, $vars);
	
	//URL组装
	//入口文件
	if($_SERVER['SCRIPT_NAME'] != '/index.php'){
		$script = $_SERVER['SCRIPT_NAME'];
	}
	
	//尽可能的使用pathinfo模式生成
	if(C('URL_MODEL') > 0 || defined('CLIWORKING') || !empty($_SERVER['PATH_INFO'])){
		//受保护的值
		$protectArgs = is_array(G('protectArgs')) ? G('protectArgs') : [];
		
		$url = $url . $script . '/';
		if(count($get) <= 3 & $get['m'] == 'Index' && $get['a'] == 'index'){
			unset($get['m']);
			unset($get['a']);
			if($get['g'] == 'Home' || empty($get['g'])){
				return $url;
			}else{
				return $url . $get['g'] . '/';
			}
		}
		
		$url .= (isset($get['g']) ? $get['g'] . $urlDepr : '') . $get['m'] . $urlDepr . $get['a'] . $urlDepr;
		unset($get['g']);
		unset($get['m']);
		unset($get['a']);
		
		if(count($get) > 0){
			//跳过空值 和 受保护的值
			foreach($get as $key=>$value){
				if(empty($value) || in_array($key, $protectArgs)){
					if(empty($value))
						unset($get[$key]);
					
					continue;	
				}else{
					unset($get[$key]);
					$url .= $key . $urlDepr . $value . $urlDepr;
				}
			}
		}
		$url = rtrim($url, $urlDepr) . (($urlDepr != '/' && $suffix == '/') ? C('URL_HTML_SUFFIX') : $suffix);
		
		if(count($get) > 0){
			$url .= '?' . http_build_query($get);
		}
	}else{
		$url = $url . (empty($script) ? '/' : $script) . '?' . http_build_query($get);
		unset($get);
	}
	
	//锚文本
	empty($last) OR $url .= '#' . $last;
	
	//替换域名
	if(!empty($do)){
		$do = str_replace(['AA&&BB', 'A&B'], ['https:://', 'http://'], $do);
		$host = stripos($do, 'http') === false ? $_SERVER['HTTP_HOST'] : $domain;
		if($host != $do){
			$url = str_replace($host, $do, (stripos($url, $_SERVER['HTTP_HOST']) === false ? $domain . $url : $url));
		}
	}
	
	//兼容js对接参数
	$url = str_replace(['%2B%26', '%26%2B'],['"+ ',' +"'], $url);
	
	//静态路由
	$maps = (is_array(C('URL_MAP_RULES')) && C('URL_MAP_RULES')) ? C('URL_MAP_RULES') : [];
	foreach($maps as $map=>$rule){
		if(stripos($url, str_replace('/', $urlDepr, $rule)) === false)
			continue;
		
		$url = str_replace(str_replace('/', $urlDepr, $rule), str_replace('/', $urlDepr, $map), $url);
	}
	
	return $url . (empty($get) ? '' : '?' . http_build_query($get));
}

/**
 * 内存使用计算
 */
function usageMemory(int $startMemory = 0): string
{
	$startMemory === 0 && $startMemory = C('startMemory');
	return sizeCount(memory_get_usage() - $startMemory);
}

/**
 * 压缩zip
 */
function unZip(string $path, string $root = ''): bool
{
	if(empty($path)) return false;
	
	//zip对象
	$zip = N('ZipArchive');
	
	//解压目标对象为空则按照文件名创建文件夹
	if(empty($root)){
		$root = dirname($path) . '/' . str_replace('.zip', '', basename($path));
	}
	N('Dir')::create(dirname($root));
	
	if($zip->open($path) === true){
		$zip->extractTo($root);
		return $zip->close();
	}else{
		return false;
	}
}

/**
 * 模板输出
 */
function view(string $template = null, $vars = [],$data = '', $code = 200): View
{
	return N('Respond')::create($template, 'view', $code)->assign($vars)->output($data);
}

/**
 * 引入非规则类库
 */
function vendor(string $path,string $startpath = '',string $suffix = '.php'): bool
{
	//基础路径转换
	$path = explode('.', $path);
	
	//默认开始路径
	empty($startpath) && $startpath = SOURCEPATH . 'Vendor/';
	
	//文件路径
	$path = $startpath . implode('/', $path) . $suffix;
	
	return requireCache($path);
}

/**
 * 验证码生成
 *
 *@param 字符串
 *@param 存储键值
 *@param 图片高度
 *@param 图片宽度
 * @return image 二维码图片
 */
function verificationCode(string $code = '',string $key = '',int $height=27,int $width=80): void
{
	$code = (is_null($code) || empty($code)) ? getRandomStr(4, 2) : $code;
	$len = strlen($code);
	ob_clean();
	if(defined('CLIWORKING')){
		headerLoading('Content-Type: image/png');
	}else{
		header('Content-type: image/png');
	}
	$width = (is_null($width) || empty($width)) ? $len * 20: $width;
	$fontsize = $width/$len-2;
	$im = @imagecreatetruecolor($width, $height) or die('create image error!');
	$background_color = imagecolorallocate($im,255, 255, 255);
	imagefill($im, 0, 0, $background_color);  
	for ($i = 0; $i < 10; $i++){//底色随机小字符            
		$color = imagecolorallocate($im, mt_rand(0,255),mt_rand(0, 255),mt_rand(0, 255));
		$mixcode = getRandomStr(1, 0);
		imagestring($im,0,rand(1,$width-1),rand(1,$height-2),$mixcode,$color);
	}
	for ($i = 0; $i < 100; $i++){        
		$color = imagecolorallocate($im, mt_rand(0,255),mt_rand(0, 255),mt_rand(0, 255));
		imagesetpixel($im,rand(1,$width-2),rand(1,$height-2),$color);
	}
	$border_color = imagecolorallocate($im, 160, 160, 160);   
	imagerectangle($im, 0, 0, $width-1, $height-1, $border_color);//画矩形，边框颜色200,200,200
	for ($i = 0; $i < $len; $i++){//写入随机字串
		$text_color = imagecolorallocate($im,mt_rand(30, 140),mt_rand(30,140),mt_rand(30,140));
		imagestring($im,5,$i*$fontsize+10,rand(1,$height/3),$code[$i],$text_color);
	}
	headerLoading('Content-Type: image/jpeg');
	headerLoading('Cache-Control: no-cache,must-revalidate');   
	headerLoading('Pragma: no-cache');   
	headerLoading("Expires: -1"); 
	headerLoading('Last-Modified: '.gmdate('D, d M Y 00:00:00', gmTime()).' GMT');
	imagejpeg($im);//显示图
	imagedestroy($im);//销毁图片
	$key = (is_null($key) || empty($key)) ? 'code' : $key;
	session($key, strtoupper($code));
}

/**
 * log记录
 * 
 * @param $message 要记录的内容
 * @param $type 记录类型
 * @param $path 记录路径
 */
function writeLog(string $message,int $type=0,string $path = ''): void
{
	if(!empty($message))
		\Candy\Core\Log::input($message, $type, $path);
}

/**
 * 框架析构函数
 */
function __destructor(): void
{
	if(defined('CLIWORKING') === false){
		//非Cli模式下保存G用于跨域
		$frameVariableForG = G();
		$frameVariableForG === C('frameVariableForG') OR F('frameVariableForG', $frameVariableForG, 0);
	}
	
	//添加记录
	if(function_exists('addLogs') && C('actinfo.log'))
		addLogs();
}
